package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParams;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    @Autowired
    private RestHighLevelClient client;

    @Override
    public PageResult searchList(RequestParams requestParams) {

        try {

            SearchRequest request = new SearchRequest("hotel");

            buildBasicQuery(request, requestParams);

            //分页条件
            request.source().from((requestParams.getPage() - 1) * requestParams.getSize())
                    .size(requestParams.getSize());

            //按照位置进行排序
            //requestParams.getLocation() != null && !requestParams.getLocation().equals("")
            if (requestParams.getLocation() != null && !requestParams.getLocation().equals("")) {
                request.source().sort(SortBuilders
                                         .geoDistanceSort("location", new GeoPoint(requestParams.getLocation()))
                                         .unit(DistanceUnit.KILOMETERS)
                                         .order(SortOrder.ASC));
            }
            //价格排序
            if (requestParams.getSortBy().equals("price")){
                request.source().sort(SortBuilders
                                      .fieldSort("price")
                                      .order(SortOrder.ASC));
            }
            if (requestParams.getSortBy().equals("score")){
                request.source().sort(SortBuilders
                        .fieldSort("score")
                        .order(SortOrder.DESC));
            }
            //搜索字段高亮
            request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));

            SearchResponse response = client.search(request, RequestOptions.DEFAULT);

            return handleResponse(requestParams, response);

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    //查询条件构建
    private void buildBasicQuery(SearchRequest request, RequestParams requestParams) {
        //构造BoolQueryBuilder
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //关键字搜索  如果传的参数key为空则查询所有集合
        if (requestParams.getKey() == null || "".equals(requestParams.getKey())) {
//                request.source().query(QueryBuilders.matchAllQuery());
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        } else {
//                request.source().query(QueryBuilders.matchQuery("all", requestParams.getKey()));
            boolQueryBuilder.must(QueryBuilders.matchQuery("all", requestParams.getKey()));
        }
        //过滤城市搜索
        if (requestParams.getCity() != null && !"".equals(requestParams.getCity())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("city", requestParams.getCity()));
        }
        //过滤品牌搜索
        if (requestParams.getBrand() != null && !requestParams.getBrand().equals("")) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", requestParams.getBrand()));
        }
        //过滤星际搜索
        if (requestParams.getStarName() != null && !requestParams.getStarName().equals("")) {
            System.out.println(requestParams.getStarName());
            boolQueryBuilder.filter(QueryBuilders.termQuery("starName.keyword", requestParams.getStarName()));
        }
        //过滤价格
        if (requestParams.getMaxPrice() != null && requestParams.getMinPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price")
                    .gte(requestParams.getMinPrice())
                    .lte(requestParams.getMaxPrice()));
        }

        //   控制算分
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
                boolQueryBuilder,    //原始查询条件 获取原始算分
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                //过滤条件
                                QueryBuilders.termQuery("isAD",true),
                                //算分函数
                                ScoreFunctionBuilders.weightFactorFunction(10)
                        )
                }
        );



        request.source().query(functionScoreQuery);

    }

    //解析结果
    private PageResult handleResponse(RequestParams requestParams, SearchResponse response) {
        SearchHits searchHits = response.getHits();

        //数据总条数total
        long total = searchHits.getTotalHits().value;
        //获取真实数据
        SearchHit[] hits = searchHits.getHits();
        List<HotelDoc> list = new ArrayList<>();
        for (SearchHit hit : hits) {
            //获取json数据并进行格式化
            String hitJson = hit.getSourceAsString();
            HotelDoc hotelDoc = JSON.parseObject(hitJson, HotelDoc.class);
            //对结果进行高亮显示
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (highlightFields != null && !highlightFields.isEmpty()) {
                //获取高亮数据
                HighlightField field = highlightFields.get("name");
                //将高亮数据  由数组拼接为字符串
                String name = StringUtils.join(field.getFragments());

                hotelDoc.setName(name);
            }
            Object[] sortValues = hit.getSortValues();
            if (sortValues.length > 0){
                hotelDoc.setDistance(sortValues[0]);
            }
            //将序列化后的对象添加到集合中
            list.add(hotelDoc);
        }
        return new PageResult(total, list);
    }
}
